package sketch.seg.cd.exp;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import sketch.ink.SampledPoint;
import sketch.ink.Stroke;
import sketch.shape.geom.prim.Point;
import sun.net.smtp.SmtpProtocolException;

public class CDStroke {

	private List<SampledPoint> points;

	private List<SampledPoint> corners;

	private List<Point> marks;

	public CDStroke() {
		points = new ArrayList<SampledPoint>();
		corners = new ArrayList<SampledPoint>();
		marks = new ArrayList<Point>();
	}

	public static CDStroke get(Stroke stroke) {
		CDStroke cds = new CDStroke();
		cds.points = stroke.getPoints();
		return cds;
	}

	public void draw(Graphics2D g2) {
		g2.setColor(Color.BLACK);
		g2.setStroke(new BasicStroke(2));

		SampledPoint p1 = getPoint(0);
		for (int i = 1; i < length(); i++) {
			SampledPoint p2 = getPoint(i);
			g2.drawLine((int) p1.getX(), (int) p1.getY(),
						(int) p2.getX(), (int) p2.getY());
			p1 = p2;
		}

		int W = 4, W2 = 2 * W;
		g2.setColor(Color.RED);
		g2.setStroke(new BasicStroke(W));

    	for (Point p : getMarks()) {
    		g2.fillOval(p.intX()-W, p.intY()-W, W2, W2);
    	}

    	g2.setColor(Color.BLUE);
		g2.setStroke(new BasicStroke(W));

    	for (SampledPoint pp : getCorners()) {
    		Point p = pp.toPoint();
    		g2.fillOval(p.intX()-W, p.intY()-W, W2, W2);
//    		g2.drawString(Integer.toString(getCorners().indexOf(pp)), p.intX(), p.intY());
    	}
	}

	public List<Point> getMarks() {
		return marks;
	}

	public SampledPoint getPoint(int i) {
		return points.get(i);
	}

	public int length() {
		return points.size();
	}

	public String toString() {
		StringBuffer buf = new StringBuffer();
		buf.append("Stroke:");
		for (SampledPoint point : getPoints()) {
			buf.append(point.toSimpleML());
		}
		buf.append(":");
		for (Point mark : getMarks()) {
			buf.append(mark.toString());
		}
		return buf.toString();
	}

	public List<SampledPoint> getPoints() {
		return points;
	}

	public static CDStroke parseStroke(String string) {
		String[] parts = string.split(":");

//		if (parts.length != 3)
//			return null;

		CDStroke stroke = new CDStroke();

		String seq = parts[1];
		int state = 0; // 0 : before (
		               // 1 : between ( and )
		int prev = 0;
		for (int i = 0; i < seq.length(); i++) {
			if (seq.charAt(i) == '(' && state == 0) {
				state = 1;
				prev = i;
			} else if (seq.charAt(i) == ')' && state == 1) {
				state = 0;
				stroke.appendPoint(SampledPoint.parseSimpleML((seq.substring(prev, i+1))));
			}
		}

		if (parts.length < 3)
			return stroke;

		seq = parts[2];
		state = 0; // 0 : before (
		               // 1 : between ( and )
		prev = 0;
		for (int i = 0; i < seq.length(); i++) {
			if (seq.charAt(i) == '(' && state == 0) {
				state = 1;
				prev = i;
			} else if (seq.charAt(i) == ')' && state == 1) {
				state = 0;
				stroke.appendMark(Point.parsePoint((seq.substring(prev, i+1))));
			}
		}

		return stroke;
	}

	public void appendMark(Point mark) {
		getMarks().add(mark);
	}

	public void appendPoint(SampledPoint point) {
		getPoints().add(point);
	}

	public void mark(Point point) {
//		System.out.println("check marking at: " + point.toString());
		if (containsMark(point) != null)
			return;

		SampledPoint p = nearestNeighbour(point);
		if (p != null && near(p.toPoint(), point)) {
			Point mark = p.toPoint();
			if (near(mark, point)) {
				appendMark(mark);
				System.out.println("add mark: " + mark.toString());
			}
		}
	}

	public SampledPoint nearestNeighbour(Point point) {
		double min = Double.MAX_VALUE;
		SampledPoint mp = null;
		for (SampledPoint p : getPoints()) {
			double d = p.toPoint().distance(point);
			if (d < min) {
				min = d;
				mp = p;
			}
		}
		return mp;
	}

	public Point containsMark(Point point) {
		for (Point p : getMarks()) {
			if (near(p, point))
				return p;
		}
		return null;
	}

	public boolean near(Point p1, Point p2) {
		double dis = p1.distance(p2);
//		System.out.println("cal dis between " + p1.toString() + " and " + p2.toString() + ": " + dis);
		return dis < 10;
	}

	public boolean near(SampledPoint p1, SampledPoint p2) {
//		double dis = p1.toPoint().distance(p2.toPoint());
//		System.out.println("cal dis between " + p1.toString() + " and " + p2.toString() + ": " + dis);
//		return dis < 30;
		return near(p1.toPoint(), p2.toPoint());
	}

	public boolean identical(Point p1, Point p2) {
		return p1.distance(p2) < 5;
	}

	public void setCorners(List<SampledPoint> cs) {
		corners = cs;
	}

	public List<SampledPoint> getCorners() {
		return corners;
	}

	public int _n_of_marks() {
		return getMarks().size();
	}

	public int _n_of_corners() {
		if (getCorners().size() < 1)
			return 0;
		SampledPoint tmp = getCorners().get(0);

		int i = 1;
		if (near(tmp, getPoint(0)))
			i--;
		for (Iterator<SampledPoint> it = getCorners().iterator(); it.hasNext(); ) {
			SampledPoint cur = it.next();
			if (!near(tmp, cur))
				i++;
			tmp = cur;
		}


		if (near(tmp, getPoint(getPoints().size()-1)))
			i--;

		return i;
	}

	public int _n_of_success() {
		int n = 0;
		boolean[] bits = new boolean[_n_of_marks()];
		for (int i = 0; i < bits.length; i++) {
			bits[i] = false;
		}

		for (SampledPoint pp : getCorners()) {
			Point p = pp.toPoint();

			int i = getMarks().indexOf(containsMark(p));
			if (i >= 0 && bits[i] == false) {
				n++;
				bits[i] = true;
			}
		}
		return n;
	}
}
